#include <iostream>
#include <fstream>
#include <queue>
#include <list>
#include <sstream>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <boost/tr1/memory.hpp>

#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>

#include <ctime>
#include <cstdio>

#include "URLDiskClass.h"
#include "../classes_net/URL.h"

using namespace std;

URLDiskClass::URLDiskClass(){
	pDisk[0] = fopen64 ("DiscoURL_P0.bin","ab+");  //Leitura e "escrita no final"
	pDisk[1] = fopen64 ("DiscoURL_P1.bin","ab+");  //Leitura e "escrita no final"
	pDisk[2] = fopen64 ("DiscoURL_P2.bin","ab+");  //Leitura e "escrita no final"
	pDisk[3] = fopen64 ("DiscoURL_P3.bin","ab+");  //Leitura e "escrita no final"
	pDisk[4] = fopen64 ("DiscoURL_P4.bin","ab+");  //Leitura e "escrita no final"
	pDisk[5] = fopen64 ("DiscoURL_P5.bin","ab+");  //Leitura e "escrita no final"
	
	for(int i=0; i<6; i++)
	    if (pDisk[i] == NULL){
	    	cout << "Erro ao abrir o arquivo de disco pDisk: "<< i << endl;
	    	exit(1);
	    }
	
    pSaveDiskReadPos[0] = fopen64 ("pSaveDiskReadPos_P0","r+");  //Leitura e escreve
    pSaveDiskReadPos[1] = fopen64 ("pSaveDiskReadPos_P1","r+");  //Leitura e escreve
    pSaveDiskReadPos[2] = fopen64 ("pSaveDiskReadPos_P2","r+");  //Leitura e escreve
    pSaveDiskReadPos[3] = fopen64 ("pSaveDiskReadPos_P3","r+");  //Leitura e escreve
    pSaveDiskReadPos[4] = fopen64 ("pSaveDiskReadPos_P4","r+");  //Leitura e escreve
    pSaveDiskReadPos[5] = fopen64 ("pSaveDiskReadPos_P5","r+");  //Leitura e escreve
    
    for(int i=0; i<6; i++)
	    if (pSaveDiskReadPos[i] == NULL){
	    	cout << "Erro ao abrir o arquivo de disco pSaveDiskReadPos: " << i << endl;
	    	exit(1);
	    }
    
    URLBuffer_IN = new _t_URL;
    URLBuffer_OUT = new _t_URL;
}

URLDiskClass::~URLDiskClass(){
	 
	for(int i=0; i<6; i++)	 
		if(pDisk[i] != NULL) fclose (pDisk[i]);
	
	for(int i=0; i<6; i++)
		if(pSaveDiskReadPos[i] != NULL) fclose (pSaveDiskReadPos[i]);
	
	delete URLBuffer_IN;
	delete URLBuffer_OUT;
}


void URLDiskClass::writeDisk(URL t){
//	boost::mutex::scoped_lock urlDiskClassLock(urlDiskClassMutex);
	try{
		string _path;
		string _dominio;
		string _protocolo;
		_path = (t.get_path().size() >= 230) ? t.get_path().substr(0,230) : t.get_path();
		_dominio = (t.get_dominio().size() >= 60) ? t.get_dominio().substr(0,60) : t.get_dominio();
		_protocolo = (t.get_protocol().size() >= 10) ? t.get_protocol().substr(0,10) : t.get_protocol();
		
		int res;
		int nivel;
		
		//coloca no disco correto
		nivel = t.getDepth();
		if(nivel < 0) nivel = 0;
		if(nivel > 5) nivel = 5;
		
		//Teste
		unsigned long long posConsistente = size(nivel);
		{
		boost::mutex::scoped_lock lock(pDiskMutex[nivel]);
		fseeko64 ( pDisk[nivel] , posConsistente*sizeof(_t_URL) , SEEK_SET); //posiciona
		boost::mutex::scoped_lock blockLock(urlDiskBufferOutMutex);
		strcpy(URLBuffer_OUT->t_path, _path.c_str());
		strcpy(URLBuffer_OUT->t_dominio, _dominio.c_str());
		strcpy(URLBuffer_OUT->t_protocol, _protocolo.c_str());
		res = fwrite(URLBuffer_OUT, sizeof(_t_URL), 1, pDisk[nivel] );
		}
	
		if(res != 1){
			cout << "Erro ao gravar em URLDiskClass::writeDisk() " <<  endl;
		}
	}catch(...){
		cout << "Erro no URLDiskClass::writeDisk " << endl;
	}
}


URL URLDiskClass::readDisk(void){
//	boost::mutex::scoped_lock urlDiskClassLock(urlDiskClassMutex);
	long long pDiskOffset;
  	long long pDiskSize = 0;
  	long long _savePos = 0;
  	
  	int id;
  	for(id=0; id<6; id++){
  	  	// Tamanho do disco
  	  	pDiskSize = size(id);
  	  	//Recupera ultima posicao lida
  	  	_savePos  = pos(id);
  	  		  	
  	  	//verifica se vazio
  	  	if(_savePos > pDiskSize) continue;
  	  	
  	  	pDiskOffset = _savePos;
  	  	_savePos = _savePos + 1;
  	  	{
  	  	boost::mutex::scoped_lock lock(pDiskMutex[id]);
  	  	boost::mutex::scoped_lock blockLock(urlDiskBufferInMutex);
  	  	fseeko64 ( pDisk[id] , pDiskOffset*sizeof(_t_URL) , SEEK_SET);
  	  	
  	  	fread(URLBuffer_IN, sizeof(_t_URL), 1, pDisk[id]);
  	  	tURL = URL(URLBuffer_IN->t_dominio, URLBuffer_IN->t_path, URLBuffer_IN->t_protocol );
  	  	}
  	  	//Grava ultima pos lida
  	  	writepos(id, _savePos);
 	  
  	    return tURL;
  	}
	tURL = URL("", "","");
	return tURL;  
}




long long URLDiskClass::size(int id){
	long long pDiskSize;
	if(id < 0) id = 0;
	if(id > 5) id = 5;
	{
	boost::mutex::scoped_lock lock(pDiskMutex[id]);
  	fseeko64 (pDisk[id] , 0 , SEEK_END); //vai para o final
  	pDiskSize = ftello64 (pDisk[id]);  	// Pega o tamanho
	}
  	pDiskSize = pDiskSize / sizeof(_t_URL);
  	return pDiskSize;
}


long long URLDiskClass::pos(int id){
	boost::mutex::scoped_lock urlDiskClassLock(urlDiskClassMutex_POS);
	long long _save_pos;
	if(id < 0) id = 0;
	if(id > 5) id = 5;
  	rewind (pSaveDiskReadPos[id]);
  	fscanf (pSaveDiskReadPos[id], "%lld", &_save_pos);
  	return _save_pos;
}

void URLDiskClass::writepos(int id, long long _save_pos){
	boost::mutex::scoped_lock urlDiskClassLock(urlDiskClassMutex_POS);
  	//Grava ultima pos lida
  	rewind (pSaveDiskReadPos[id]);
  	fprintf(pSaveDiskReadPos[id], "%lld", _save_pos); // aponta para proximo lugar a ser lido
  	fflush (pSaveDiskReadPos[id]);
}


long long URLDiskClass::totalNewURLs(void){
	long long tmTotal;
	long long tmTemp;
	
	tmTotal = 0;
	
	for(int i=0; i<6; i++){
		tmTemp = size(i) - pos(i);
		if(tmTemp >= 0) tmTotal += tmTemp;		
	}
	
	return tmTotal;
}



